package com.wallezhou.upload.servlet;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.nio.channels.FileChannel;

/**
 * 文件块合并
 */
public class FileMergeServelet extends HttpServlet {

    //数据上传的目录路径参数名
    public static String TARGETDIR = "targetDir";

    //数据上传的目录
    protected String targetDir;

    protected String getConfigParam(String key) {
        return getServletConfig().getInitParameter(key);
    }

    @Override
    public void init() {
        String targetDir = getConfigParam(TARGETDIR);
        if (targetDir != null) {
            this.targetDir = targetDir;
        }else{
            throw new IllegalArgumentException(TARGETDIR+"为FileMergeServelet的必要参数");
        }
    }

    @Override
    protected void service(HttpServletRequest servletRequest, HttpServletResponse servletResponse) throws IOException, ServletException {
        String method = servletRequest.getMethod();
        if (method.equals("OPTIONS")){
            this.doOptions(servletRequest, servletResponse);
            return;
        }
        //需要获取传入的三个参数 total: 文件分块总数  hash: 完整文件的哈希值 name: 文件名，决定上传后的文件名，文件输出在this.targetDir/hash值/name
        int total = Integer.parseInt(servletRequest.getParameter("total"));
        String hash = servletRequest.getParameter("hash");
        String name = servletRequest.getParameter("name");

        File HASHDIR = new File(this.targetDir + File.separator + hash);
        int actualTotal = this.numberOfFiles(HASHDIR);
        if(actualTotal != total){
            servletResponse.getWriter().print("切片文件数量不符合");
            return;
        }
        String[] fpaths = new String[total];
        for (int i = 0; i < total; i++) {
            fpaths[i] = this.targetDir + File.separator+ hash+File.separator + hash + "-" + i;
        }
        String mergeFilePath = this.targetDir + File.separator + hash+File.separator + name;
        this.mergeFiles(fpaths, mergeFilePath);
    }

    /**
     * 计算分块的数量
     * @param HASHDIR
     * @return
     */
    private int numberOfFiles(File HASHDIR){
        File []list = HASHDIR.listFiles();
        int fileCount = 0;
        //folderCount = 0;
        //long length = 0;
        for (File file : list){
            if (file.isFile()){
                if(!file.getName().contains(HASHDIR.getName())) continue;//如果文件不是分块文件不计入
                fileCount++;
//                length += file.length();
            }else {
                //do nothing
//                folderCount++;
            }
        }
        return fileCount;
    }

    /**
     * 合并分块
     * @param fpaths
     * @param resultPath
     * @return
     */
    private boolean mergeFiles(String[] fpaths, String resultPath) {
        if (fpaths == null || fpaths.length < 1 || isBlank(resultPath)) {
            return false;
        }
        if (fpaths.length == 1) {
            return new File(fpaths[0]).renameTo(new File(resultPath));
        }

        File[] files = new File[fpaths.length];
        for (int i = 0; i < fpaths.length; i ++) {
            files[i] = new File(fpaths[i]);
            if (isBlank(fpaths[i]) || !files[i].exists() || !files[i].isFile()) {
                return false;
            }
        }

        File resultFile = new File(resultPath);
        if (resultFile.exists()) return true;//已存在不重复上传

        try {
            FileChannel resultFileChannel = new FileOutputStream(resultFile, true).getChannel();
            for (int i = 0; i < fpaths.length; i ++) {
                FileChannel blk = new FileInputStream(files[i]).getChannel();
                resultFileChannel.transferFrom(blk, resultFileChannel.size(), blk.size());
                blk.close();
            }
            resultFileChannel.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
            return false;
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }

        for (int i = 0; i < fpaths.length; i ++) {
            files[i].delete();
        }

        return true;
    }

    private boolean isBlank(String str) {
        int strLen;
        if (str != null && (strLen = str.length()) != 0) {
            for(int i = 0; i < strLen; ++i) {
                if (!Character.isWhitespace(str.charAt(i))) {
                    return false;
                }
            }

            return true;
        } else {
            return true;
        }
    }
}
